import java.io.File;
import java.io.FileOutputStream;

/*
 * Album/Artist mean.
 * For tracks it takes the album mean. For albums it takes the artist mean.
 */
public class AAAM extends Scheduler implements Runnable {
	int start;
	int stop;
	byte[] BLOCK;

	final double NONE = Double.NaN;

	public AAAM(int start, int stop) {
		this.start = start;
		this.stop = stop;
	}

	void writeBlock() {
		String FILE_NAME = String.format("%s%07d.bin", BLOCK_PATH, start);
		File out = new File(FILE_NAME);
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(out);
			fos.write(BLOCK);
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("Done writing: " + FILE_NAME + " @ "
				+ this.getClass());
	}

	@Override
	public void run() {
		int ilen = USERB[0].ilen;
		BLOCK = new byte[(stop - start) * ilen];

		int c = 0;
		for (int bUserId = start; bUserId < stop; bUserId++) {
			for (int i = 0; i < ilen; i++) {
				int bItemId = USERB[bUserId].items[i];
				byte pred = predict(bUserId, bItemId);

				BLOCK[c * ilen + i] = pred;
			}
			c++;
		}
		writeBlock();
	}

	private byte predict(int bUserId, int bItemId) {
		double mean = 0;
		Item bItem = ITEMA[bItemId];
		if (bItem instanceof Track) {
			Track bTrack = (Track) bItem;
			Album bItemAlbum = bTrack.album;
			if (bItemAlbum != null) {
				int[] itemIds = USERA[bUserId].items;
				mean = getAlbumMean(itemIds, bItemAlbum);
				if (mean != NONE) {
					return (byte) mean;
				} else {
					return (byte) bItemAlbum.mean;
				}
			}
		} else if (bItem instanceof Album) {
			Album bAlbum = (Album) bItem;
			Artist bItemArtist = bAlbum.artist;
			if (bItemArtist != null) {
				int[] itemIds = USERA[bUserId].items;
				mean = getArtistMean(itemIds, bItemArtist);
				if (!Double.isNaN(mean)) {
					return (byte) mean;
				} else {
					return (byte) bItemArtist.mean;
				}
			} 
		}
		return (byte) USERA[bUserId].mean;
	}

	private double getAlbumMean(int[] itemIds, Album bItemAlbum) {
		double albumMean = 0;
		int n = 0;
		boolean set = false;
		for (int itemID : itemIds) {
			if (ITEMA[itemID] instanceof Track
					&& bItemAlbum.equals(((Track) ITEMA[itemID]).album)) {
				albumMean += ITEMA[itemID].mean;
				n++;
				set = true;
			}
		}
		if (set) {
			return albumMean/n;
		} else {
			return Double.NaN;
		}
	}

	private double getArtistMean(int[] itemIds, Artist bItemArtist) {
		double artistMean = 0;
		int n = 0;
		boolean set = false;
		for (int itemID : itemIds) {
			if (ITEMA[itemID] instanceof Album
					&& bItemArtist.equals(((Album) ITEMA[itemID]).artist)) {
				artistMean += ITEMA[itemID].mean;
				n++;
				set = true;
			}
		}
		if (set) {
			return artistMean/n;
		} else {
			return Double.NaN;
		}
	}
}
